#include <math.h>
#include "Bone.h"

Bone::Bone(Bone * parent, float angle, float length, float tbShift, TextureBox * textureBox)
{
	this->parent = parent;
	this->angle = angle;
	this->length = length;
	this->textureBox = textureBox;
	this->tbShift = tbShift;
	controlLock = false;
	displayMode = 0;
	UnClick();
	//use angle, length and parent to determine endpoint
	
	outgoingBones = NULL;
	CalculatePosition();
}
Bone::Bone(Bone * parent, float angle, float length, TextureBox * textureBox)
{
	this->parent = parent;
	this->angle = angle;
	this->length = length;
	this->textureBox = textureBox;
	this->tbShift = 0.5;
	controlLock = false;
	displayMode = 0;
	UnClick();
	//use angle, length and parent to determine endpoint
	
	outgoingBones = NULL;
	CalculatePosition();
}

Bone::Bone(Bone * parent, Vertex endPoint, TextureBox * textureBox)
{
	printf("Making new bone\n");
	this->parent = parent;
	this->textureBox = textureBox;
	this->endPoint = endPoint;
	this->tbShift = 0.5;
	controlLock = false;
	displayMode = 0;
	UnClick();
	
	outgoingBones = NULL;
	CalculateAngleLength();
	
	printf("Done making new bone\n");
}

Bone::~Bone()
{
	if(outgoingBones)
	{
		delete(outgoingBones);
		outgoingBones = NULL;
	}
	if(textureBox)
	{
		delete(textureBox);
		textureBox = NULL;
	}
}
void Bone::Remove(Bone * bone)
{
	if(outgoingBones)
	{
		outgoingBones->Remove(bone);	
	}
}
void Bone::RemoveMe()
{
	if(parent)
		parent->Remove(this);	
}
void Bone::SetTile(Tile * tile)
{
	textureBox->SetTile(tile);
}
void Bone::CalculatePosition()
{
	Vector2D direction = Vector2D(1,0);
	Vector2D startPoint = Vector2D(0,0);
	if(parent)
	{
		startPoint = parent->GetEndPoint().GetPosition();
		if(parent->GetParent())
		{
			Vector2D gpEndPoint = parent->GetParent()->GetEndPoint().GetPosition();
			direction = !(startPoint - gpEndPoint);
		}	
		else
		{
			direction = !startPoint;
		}
	}
	//rotate direction by angle
	float sinTheta = sin(this->angle);
	float cosTheta = cos(this->angle);
	float newX = direction.x * cosTheta - direction.y * sinTheta;
	float newY = direction.y * cosTheta + direction.x * sinTheta;
	direction.x = newX;
	direction.y = newY;
	
	this->endPoint = Vertex(startPoint + direction * this->length);
	if(outgoingBones)
		outgoingBones->CalculatePosition();
}
void Bone::CalculateAngleLength()
{
	printf("CalculateAngleLength()\n");
	Vector2D startPoint = Vector2D(0,0);
	Vector2D direction = Vector2D(1,0);
	//find angle and length from endPoint and parent
	if(parent)
	{
		startPoint = parent->GetEndPoint().GetPosition();
		if(parent->GetParent())
		{
			Vector2D gpEndPoint = parent->GetParent()->GetEndPoint().GetPosition();
			direction = !(startPoint - gpEndPoint);
		}
		else
		{
			direction = !startPoint;
		}
	}	
	Vector2D direction2 = !(endPoint.GetPosition() - startPoint);
	angle = acos(direction.dot(direction2));
	if(direction.AngleDirection(direction2) < 0)
	{
		angle = -angle;
	}
	
	if(controlLock)
	{
		this->endPoint = Vertex(startPoint + direction2 * this->length);
	}
	else
	{
		length = startPoint.dist(endPoint.GetPosition());
	}
	
	if(outgoingBones)
	{
		outgoingBones->CalculatePosition();
	}
}
void Bone::AddBone(Bone * bone)
{
	if(controlLock)
	{
		if(bone)
		{	
			delete(bone);
			bone = NULL;
		}
	}
	else
	{
		if(outgoingBones)
		{
			outgoingBones->Add(bone);
		}
		else
		{
			outgoingBones = new BoneList(bone);
		}
	}
}
void Bone::RemoveBone(Bone * bone)
{
	if(outgoingBones)
	{
		outgoingBones->Remove(bone);
	}
}
void Bone::MoveEndPoint(Vertex endPoint)
{
	this->endPoint = endPoint;
	CalculateAngleLength();
}
Vertex Bone::GetEndPoint()
{
	return endPoint;
}
Bone * Bone::GetParent()
{
	return parent;
}
void Bone::Draw()
{
	glPushMatrix();
	glRotatef(angle*180/3.14159, 0,0,1);
	
	glColor3f(1,1,1);

	glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
	glAlphaFunc( GL_GREATER, 0.01 );
  	glEnable( GL_ALPHA_TEST );
  	glDisable(GL_TEXTURE_2D);
  	
  	glColor3f(1,1,1);
  	glPushMatrix();
	
	glTranslatef(length * (tbShift),0,0);
	if(textureBox && displayMode == 0)
	{
		textureBox->Draw();
	}
	glPopMatrix();
	glBegin(GL_LINES);
	glVertex2f(0,0);
	glVertex2f(length,0);
	glEnd();
	
	
	
	glTranslatef(length,0,0);
	endPoint.Draw();
	if(outgoingBones)
		outgoingBones->Draw();
		
	
	glPopMatrix();
	
}
Vector2D Bone::ToTextureBoxSpace(Vector2D headPosition, Vector2D mouseSpot)
{
	float fullAngle = GetFullAngle();
	Vector2D endPointSpot = endPoint.GetPosition() + headPosition;
	Vector2D startPointSpot = headPosition;
	if(parent)
		startPointSpot = startPointSpot + parent->GetEndPoint().GetPosition();
		
	Vector2D textureBoxMidpoint = endPointSpot - startPointSpot;
	textureBoxMidpoint = startPointSpot + textureBoxMidpoint * tbShift;
	mouseSpot = mouseSpot - textureBoxMidpoint;
	float sinTheta = sin(-fullAngle);
	float cosTheta = cos(-fullAngle);
	float newX = mouseSpot.x * cosTheta - mouseSpot.y * sinTheta;
	float newY = mouseSpot.y * cosTheta + mouseSpot.x * sinTheta;
	mouseSpot.x = newX;
	mouseSpot.y = newY;
	return mouseSpot;
}
Bone * Bone::Click(Vector2D headPosition, Mouse * mouse)
{
	printf("endpoint = (%f,%f)\n", endPoint.GetPosition().x, endPoint.GetPosition().y);
	printf("headPosition = (%f, %f)\n", headPosition.x, headPosition.y);
	
	if(((endPoint.GetPosition() + headPosition) | mouse->GetPosition()) < 0.2)
	{
		//clicked the endpoint
		printf("CLICKED!\n");
		holdingTextureBox = false;
		holdingTbCp = false;
		holdingEndpoint = true;	
		return this;
	}
	
	if(outgoingBones)
	{ 
		Bone * result = outgoingBones->Click(headPosition, mouse);
		if( result)
			return result;
	}
	//check if we clicked the texture box. we need to find the mouse spot in bone space
	if(!controlLock)
	{
		Vector2D mouseSpot = ToTextureBoxSpace(headPosition, mouse->GetPosition());
		
		if(textureBox->Click(mouseSpot))
		{
			holdingTextureBox = false;
			holdingTbCp = true;
			holdingEndpoint = false;
			return this;
		}
			
		Vector2D size = textureBox->GetSize();
		if(mouseSpot.x > -size.x/2 && mouseSpot.x < size.x/2 &&
		   mouseSpot.y > -size.y/2 && mouseSpot.y < size.y/2)
		{
			printf("CLICKED Texture box\n");
			holdingEndpoint = false;
			holdingTbCp = false;
			holdingTextureBox = true;
			return this;  	
		}
	}
	
	
	return NULL;
}
void Bone::UnClick()
{
	holdingEndpoint = false;
	holdingTextureBox = false;
	holdingTbCp = false;
}
float Bone::GetFullAngle()
{
	float fullAngle = angle;
	Bone * search = parent;
	while(search)
	{
		fullAngle += search->GetAngle();
		search = search->parent;
	}
	return fullAngle;
}
float Bone::GetAngle()
{
	return angle;
}
bool Bone::Drag(Vector2D headPosition, Mouse * mouse)
{
	if(holdingEndpoint)
	{
		MoveEndPoint(mouse->GetPosition() - headPosition);	
		//if(outgoingBones)
		//	outgoingBones->Drag(headPosition, mouse);
	}	
	else if(holdingTextureBox)
	{
		//we need to convert the mouse spot into bone space coordinates
		Vector2D mouseSpot = ToTextureBoxSpace(headPosition,mouse->GetPosition());
		tbShift += mouseSpot.x/length;
	}
	else if(holdingTbCp)
	{
		textureBox->Drag(ToTextureBoxSpace(headPosition,mouse->GetPosition()));
	}
	return false;
}
char * Bone::GetTag()
{
	printf("Bone::GetTag()\n");
	char * textureTag = textureBox->GetTag();
	int numOutgoing = 0;
	if(outgoingBones)
		numOutgoing =outgoingBones->GetLength();
		
	char * tag = (char *)malloc(sizeof(char)*(strlen(textureTag)+40));
	sprintf(tag, "%s,%.3f,(%.3f,%.3f),%d",textureTag,tbShift,length,angle*180/3.14159,numOutgoing);
	free(textureTag);
	textureTag = NULL;
	
	if(outgoingBones)
	{
		char * childTag = outgoingBones->GetTag();
		tag = (char*)realloc(tag, sizeof(char) * (strlen(tag)+strlen(childTag)+2));
		sprintf(tag,"%s,%s",tag,childTag);
	}	
	printf("Done Bone::GetTag()\n");
	return tag;
}
char * Bone::GetPose()
{
	printf("Bone::GetPose()\n");
	char * textureTag = textureBox->GetTag();
	int numOutgoing = 0;
	if(outgoingBones)
		numOutgoing =outgoingBones->GetLength();
		
	char * tag = (char *)malloc(sizeof(char)*(strlen(textureTag)+30));
	sprintf(tag, "(%.3f,%.3f,0.1),%d",length,angle*180/3.14159,numOutgoing);
	free(textureTag);
	textureTag = NULL;
	
	if(outgoingBones)
	{
		char * childTag = outgoingBones->GetPose();
		tag = (char*)realloc(tag, sizeof(char) * (strlen(tag)+strlen(childTag)+2));
		sprintf(tag,"%s,%s",tag,childTag);
	}	
	printf("Done Bone::GetPose()\n");
	return tag;
}
void Bone::RotateClockwise()
{
	textureBox->RotateClockwise();
}
void Bone::RotateAntiClockwise()
{
	textureBox->RotateAntiClockwise();
}
void Bone::ToggleDisplay()
{
	displayMode =(displayMode+1)%MAX_DISPLAY_MODES;
	if(outgoingBones)
		outgoingBones->RunFunction(&Bone::ToggleDisplay);
}
void Bone::ToggleLock()
{
	controlLock = !controlLock;
	if(outgoingBones)
		outgoingBones->RunFunction(&Bone::ToggleLock);
}
